Traits (תכונות) דומות מאוד למחלקות ומאפשרות להגדיר פונקציות שמחלקות אחרות יוכלו להשתמש בהם בלי שכפול של קוד ורק מגרסה php 5.4
הרחבת מחלקה עם תכונות
trait כביכול הוא אופן רישום של פעולות המחלקה מחוץ למחלקה. די דומה באופן פעולתו לאינקלוד של סקריפט אחר לתוך הקובץ הנוכחי, שמתפקד כאילו העתיקו את התוכן של הקובץ החיצוני לתוך הקובץ הנוכחי.
traits עובדים באופן זהה, כאילו העתיקו את הקוד של ה-trait למחלקה הנוכחית:
trait world {
public function sayWorld() {
echo 'World!';
}
}
class hello {
use world; // <--- include the external trait code here
public function say() {
echo 'Hello ', $this->sayWorld();
}
}
$o = new hello();
$o->say(); // echos Hello World!
public function sayWorld() {
echo 'World!';
}
}
class hello {
use world; // <--- include the external trait code here
public function say() {
echo 'Hello ', $this->sayWorld();
}
}
$o = new hello();
$o->say(); // echos Hello World!
כמה מחלקות יכולות להשתמש באותו Trait
trait אחד יכול לשמש כמה מחלקות ואין צורך שיהיו קשורות אחת לשנייה בדרך כלשהי, כמו שכמה קבצים שונים ולא קשורים יכולים לעשות אינקלוד לאותו קובץ.
trait world {
public function sayWorld() {
echo 'World!';
}
}
class hello {
use world;
public function say() {
echo 'Hello ', $this->sayWorld();
}
}
class hi
{
use world; // another class is using the same trait
public function say()
{
echo 'Hi', $this->sayWorld();
}
}
$o = new hello();
$o->say(); // echos Hello World!
$o2 = new hi();
$o2 -> say(); // echos Hi World!
public function sayWorld() {
echo 'World!';
}
}
class hello {
use world;
public function say() {
echo 'Hello ', $this->sayWorld();
}
}
class hi
{
use world; // another class is using the same trait
public function say()
{
echo 'Hi', $this->sayWorld();
}
}
$o = new hello();
$o->say(); // echos Hello World!
$o2 = new hi();
$o2 -> say(); // echos Hi World!
כל מחלקה יכולה להשתמש בכמה traits במקביל
אפשרות זו מונעת את הצורך בירושה כפולה ומאפשרת horizontal code reuse
trait tBeep { public function beep() { echo 'beep'; } }
trait tDrive { public function drive() { echo 'whoooom';} }
class LicencedDriver /*extends Driver*/
{
use tBeep, tDrive;
}
class SuperCar /*extends Car*/
{
use tBeep, tDrive;
}
$ld = new LicencedDriver();
$ld->beep(); // beep
$ld->drive(); // whoooom
$sc = new SuperCar();
$sc->beep(); // beep
$sc->drive(); // whooom
trait tDrive { public function drive() { echo 'whoooom';} }
class LicencedDriver /*extends Driver*/
{
use tBeep, tDrive;
}
class SuperCar /*extends Car*/
{
use tBeep, tDrive;
}
$ld = new LicencedDriver();
$ld->beep(); // beep
$ld->drive(); // whoooom
$sc = new SuperCar();
$sc->beep(); // beep
$sc->drive(); // whooom
ירושת traits
לא ממש נכון לקרוא לזה ירושה, אבל trait אחד יכול להיות מורכב בעצמו מ traits אחרים.
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World!';
}
}
trait HelloWorld {
use Hello, World;
}
class MyHelloWorld {
use HelloWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
// Results eventually in: Hello World!
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World!';
}
}
trait HelloWorld {
use Hello, World;
}
class MyHelloWorld {
use HelloWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
// Results eventually in: Hello World!
traits overloading
שני traits יכולים להכיל פונקציה עם שם זהה. במקרה הזה php תחזיר שגיאה
Fatal error: Trait method has not been applied, because there are collisions with other trait methods on
לפתור את זה אפשר על ידי הגדרה ברורה - בפונקציה של איזה מה-traits להשתמש:
trait tOne
{
public function beep() { echo '1beep'; }
public function dooo() { echo '1dooo'; }
}
trait tTwo
{
public function beep() { echo '2beep';}
public function dooo() { echo '2dooo'; }
}
class car
{
use tOne, tTwo
{
tOne::beep insteadof tTwo; // use tOne's beep
tTwo::dooo insteadof tOne; // use tTwo's dooo
// alias for the tTwo's beep, since the default beep() is tOne's
tTwo::beep as twobeep;
}
}
$car = new car();
$car->beep(); // 1beep from tOne
$car->dooo(); // 2dooo from tTwo
$car->twobeep(); // 2beep with the alias
{
public function beep() { echo '1beep'; }
public function dooo() { echo '1dooo'; }
}
trait tTwo
{
public function beep() { echo '2beep';}
public function dooo() { echo '2dooo'; }
}
class car
{
use tOne, tTwo
{
tOne::beep insteadof tTwo; // use tOne's beep
tTwo::dooo insteadof tOne; // use tTwo's dooo
// alias for the tTwo's beep, since the default beep() is tOne's
tTwo::beep as twobeep;
}
}
$car = new car();
$car->beep(); // 1beep from tOne
$car->dooo(); // 2dooo from tTwo
$car->twobeep(); // 2beep with the alias
יש לקרוא את זה כ:
השתמש בפונקציה beep של tOne במקום בזאת של tTwo
השתמש בפונקציה dooo של tTwo במקום בזאת של tOne
תקרא לפונקציה beep של tTwo בשם חדש - twobeep
ל traits יש גישה למשתנים של המחלקה
וחוץ מזה traits יכולים להכיל פונקציות אבסטרקטיות שחייבות להיות ממומשות במחלקה שתכיל את אותו ה trait
trait polite
{
public function hi() { echo 'hi ', $this->name; }
abstract public function bye();
}
class guest
{
use polite;
private $name = 'Alex';
public function bye() { echo 'bye ', $this->name; }
}
$guest = new guest();
$guest->hi(); // hi Alex
$guest->bye(); // bye Alex
{
public function hi() { echo 'hi ', $this->name; }
abstract public function bye();
}
class guest
{
use polite;
private $name = 'Alex';
public function bye() { echo 'bye ', $this->name; }
}
$guest = new guest();
$guest->hi(); // hi Alex
$guest->bye(); // bye Alex
סיכום
trait משמש את המנוע של php רק בזמן בניית המחלקות. אחרי שהקוד "התקמפל" כבר אין הבדל בין מחלקות שנוצרו עם traits ומחלקות שנוצרו בלי. תפקידם הוא למנוע שכפול של קוד בין מחלקות שצריכות פונקציונליות זהה.
מאותה סיבה אין למחלקה גישה למשתנה המוגדר ב trait (כי trait הוא רק הזרקת קוד לתוך מחלקה בזמן הבניה (הפענוח) של הקוד ולא בזמן הריצה) וtrait יכול להשתמש במשתנים של המחלקה
תגובות לכתבה:
אחי אם 5.4 לא יצא למה אתה מפרסם מדריכים אליו?
מתישהו הוא ייצא, לא?
נו אבל בא לי כבר להישתמש בוא זה יקצר לי תקוד בהמון !!
נו ימבאס למה אתה מגלה ליפני שיצא עכשיו צריך לספור ימים :(
4 שבועות =)
+ עוד איזה שנה וקצת עד שכל האחסונים יעברו לזה
יאלה איזה מבאס אתה..
חזק ביותר, הולך לייעל הרבה עבודות, תודה.
חזק ביותר!
מתי יוצאת גירסא יציבה של 5.4?
למה שלא תפרסם מדריך לclass
כי זה נושא שאפשר לכתוב עליו ספר .
בכל מקרה כבר ראיתי את זה, חזק ביותר .
אפשר להסתדר בלי זה (ירושה)
http://phpguide.co.il/phplive?code=76
במקרה הפשוט הזה - כן. זה נקרא
הרחבה אנכית. כל קלאס מקבל את הפונקציונליות מלמעלה.
ברגע שיש לך כמה עצים של מחלקות יורשות שלא קשורות אחת לשניה
Transport -> Car -> Sport -> Porsche -> GT
Transport -> Bus -> Mini -> Honda -> XDBUS
Animal -> Domestic -> Horse -> Arab -> Donna
ואתה רוצה שגם לסוס תהיה פעולת "סע" וגם, תהיה כזו לכל ההונדות, ולא תהיה לפורש (כי זה מכונית רק לתצוגה ולא מוכרים כאלה בכלל) - אתה תסתבך עם הורשה אנכית.
בעצם גם עם לכל הרכבים תהיה פעולה "סע" ורק לסוס יש פעולה סע, כי לחתול שגם הוא חיה ביתית לא צריכה להיות פעולה שכזו.
וגם אם לכל החיות תהיה פעולת סע, הרי אתה עכשיו תצטרך ליצור עוד מחלקה עליונה מעל חיות ומעל רכבים שתכלול את הפונקציה "סע". הדבר ייצור לך תֹהוּ וָבֹהוּ קטסטרופי.
חוץ מזה שליצור אובייקט שיורש מפעולה, זה ארכיטקטורת מערכת ממש גרוע :)
מדריך מדהים אלכס, שבוע טוב.
כחלק מהשכתוב מחדש של המערכת שלי, אני משתמש בהרבה מאוד traits.
רק רציתי לציין שחסר כאן החלק שאומר שכאשר משתמשים (use) ב-trait בתוך מחלקה אפשר לא רק לשנות את השמות של מתודות מסוימות (as), אלא גם את רמת האינקפסולציה שלהן (private, protected, public).
דוגמה:
http://phpguide.co.il/phplive?code=640
אפשר לראות בדוגמה ש-as יוצר alias, כלומר מן "שכפול" של myMethod.
כאשר קראנו ל-myMethod של A לא הייתה בעיה - הוא public.
כאשר קראנו ל-myMethod של B לא הייתה בעיה - הוא public (כשעשינו עליו as פשוט שכפלנו אותו אך myMethod עצמו נשאר ללא שינוי).
כאשר קראנו ל-myAlias של B קיבלנו שגיאה שאי אפשר לקרוא למתודה פרטית (private).
אם נרצה שגם myMethod עצמו, בתוך B, יהיה private אז נצטרך להגדיר מתודת myMethod חדשה שהיא private.
לעוד מידע - יש את הדוקומנטציה המצוינת. :)
עדיין לא הבנתי מה ההבדל בין זה לבין ה-extends? כי עפ"י מה שהבנתי הם עושים את אותו הדבר.
@matan_sh
עם trait אתה "מזריק" קוד למחלקה קיימית, עם extends אתה "מרחיב" מחלקה קיימת..
נגיד יש לך מחלקה לניהול מסד נתונים (כמו PDO) ומחלקה לניהול קבצים (כמו SplFileObject).
כדי לעבוד איתן כ Singleton אתה צריך להרחיב את שתיהן (משהו כזה:
http://phpguide.co.il/phplive?code=803 )
במצב הזה אתה כבר יורש (או "מרחיב") ואתה לא יכול לרשת ממחלקה אחרת, ככה שאתה צריך לעשות "העתק-הדבק" לקוד של הפונקציה getInstance ושל המשתנה instance,
במקום לעשות "העתק-הדבק" אתה כותב trait פשוט ומזריק אותו למחלקה שלך
http://phpguide.co.il/phplive?code=806
הבנת?
@ldbrgr
תודה רבה!